// This is P4 sample source for basic_switching
#include <tofino/constants.p4>
#include <tofino/intrinsic_metadata.p4>
#include <tofino/primitives.p4>
#include <tofino/pktgen_headers.p4>
#include <tofino/stateful_alu_blackbox.p4>
#include <tofino/wred_blackbox.p4>

#include "includes/headers.p4"
#include "includes/parser.p4"
#include "includes/macro.p4"
#include "includes/tofino.p4"

#define CPU_PORT 192

header_type metadata_t {
  fields {
  do_ing_mirroring : 1;
  do_egr_mirroring : 1;
  ing_mir_ses : 10;
  egr_mir_ses : 10;
  new_pkt: 1;
  }
}

metadata metadata_t md;

//action add_cpu_header(pad1, fabric_color, fabric_qos, dst_device, dst_port_or_group, reserved1, ingress_ifindex, ingress_bd, reason_code) {
action add_cpu_header() {
    add_header(fabric_header);
    modify_field(fabric_header.packetType, FABRIC_HEADER_TYPE_CPU);
    modify_field(fabric_header.headerVersion, 0);
    modify_field(fabric_header.packetVersion, 0);
    modify_field(fabric_header.pad1, 0);
    modify_field(fabric_header.fabricColor, 0);
    modify_field(fabric_header.fabricQos, 0);
    modify_field(fabric_header.dstDevice, 50);
    modify_field(fabric_header.dstPortOrGroup, 0);
    add_header(fabric_header_cpu);
    modify_field(fabric_header_cpu.reserved, 0);
    modify_field(fabric_header_cpu.ingressIfindex, 0x2223);
    modify_field(fabric_header_cpu.ingressBd, 0x2425);
    modify_field(fabric_header_cpu.reasonCode, 0x2627);
    //    modify_field(fabric_header_cpu.ingressPort, ig_intr_md.ingress_port);
    modify_field(fabric_header_cpu.ingressPort, 0x2021);
    add_header(fabric_payload_header);
    modify_field(fabric_payload_header.etherType, ethernet.etherType);
    modify_field(ethernet.etherType, ETHERTYPE_BF_FABRIC); 
    modify_field(ig_intr_md_for_tm.ucast_egress_port, CPU_PORT);
}

table port_tbl {
    reads {
        ig_intr_md.ingress_port : exact;
    }
    actions {
        add_cpu_header;
    }
    default_action: add_cpu_header;
    size : 288;
}

action set_egress_port() {
  //    modify_field(ig_intr_md_for_tm.ucast_egress_port, fabric_header_cpu.ingressPort);
    modify_field(ig_intr_md_for_tm.ucast_egress_port, CPU_PORT);
}

table fabric_tbl {
  //      reads {
  //         ig_intr_md.ingress_port : exact;
  //  }
    actions {
      //  set_egress_port;
      add_cpu_header;
    }
    default_action: add_cpu_header;
    //    size : 288;
}

control process_ingress_knet {
    if (ig_intr_md.ingress_port != CPU_PORT) {
       apply(port_tbl);
    } else {
       apply(fabric_tbl);
    }
}

action old_pkt() {
    modify_field(fabric_header.dstDevice, 60);
}

action new_pkt() {
  modify_field(md.new_pkt, 1);
  modify_field(md.ing_mir_ses, 2);
}

table new_pkt {
  reads {
  	md.ing_mir_ses: exact;
  }
  actions {
    old_pkt;
    new_pkt;
  }
}

action pkt_gen() {
  clone_egress_pkt_to_egress(md.ing_mir_ses);
}

table pkt_gen {
  reads {
  md.new_pkt: exact;
  }
  actions {
    pkt_gen;
  }
 default_action: pkt_gen;
}

action pkt_gen_action() {
  clone_ingress_pkt_to_egress(md.ing_mir_ses);
}

table pkt_gen_table {
  actions {
    pkt_gen_action;
  }
 default_action: pkt_gen_action;
}

action set_mark_1_action() {
    modify_field(fabric_header.dstDevice, 55);
}

table set_mark_1_table {
  actions {
    set_mark_1_action;
  }
 default_action: set_mark_1_action;
}

action set_mark_2_action() {
    modify_field(fabric_header.dstDevice, 60);
}

table set_mark_2_table {
  actions {
    set_mark_2_action;
  }
 default_action: set_mark_2_action;
}


control ingress {
  //    process_ingress_knet();
   apply(fabric_tbl);
  apply(pkt_gen_table);
}

control egress {
  //  apply(new_pkt);
  //  apply(pkt_gen);
  if (standard_metadata.instance_type == 0) {
    apply(set_mark_1_table);
  }
  if (standard_metadata.instance_type ==1 or standard_metadata.instance_type == 2) {
    apply(set_mark_2_table);
  }
}
